library(tidyverse)
library(dplyr)
library(ggplot2)
library(rgdal)
library(tmap)
library(readxl)
library(ggrepel)
library(ggthemes)
library(scales)
library(sf)
library(ggmap)
library(DT)
library(ggmap)
library(htmltools)
library(leaflet)
library(quanteda)
library(stringr)
library(tidytext)
library(tidyverse)
library(tm)
library(wordcloud)
library(wesanderson)
library(qdap)
library(SnowballC)
ggmap::register_google(key = "AIzaSyB1-MjiXEIrgdT3FbflMLc8EUaQXVG3XVY")

Kickstarter Projects

str(df)
'data.frame':   125926 obs. of  24 variables:
 $ backers_count           : int  4 35 310 1 36 22 10 187 5 1 ...
 $ blurb                   : chr  "Soaps made with love, care, creativity and you in mind, directly from South Texas." "Pens made from Whiskey barrels, Jack Daniel's Maker's Mark, Wild Turkey and Jim Beam" "Finally we have a building but we need your help to kick start it and get going. We are calling EVERYONE who believes in us." "Everything home made in one store. From jewelry to small furniture to clothing to soaps bath bombs and candles "| __truncated__ ...
 $ converted_pledged_amount: int  41 2205 8861 100 1026 1495 2591 4515 48 1 ...
 $ country                 : chr  "USA" "USA" "USA" "USA" ...
 $ country_displayable_name: chr  "the United States" "the United States" "the United States" "the United States" ...
 $ created_at              : chr  "2017-12-04" "2018-06-07" "2015-09-23" "2014-10-04" ...
 $ currency                : chr  "USD" "USD" "USD" "USD" ...
 $ deadline                : chr  "2018-01-04" "2018-07-11" "2015-11-15" "2014-11-07" ...
 $ goal                    : num  150 900 8500 50000 800 12000 24000 4000 200 11900 ...
 $ id                      : int  123246984 1207132794 1899686686 1081353908 84306631 1791742083 1569598343 111033308 1023343641 234361295 ...
 $ is_starrable            : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ launched_at             : chr  "2017-12-05" "2018-06-11" "2015-10-01" "2014-10-08" ...
 $ name                    : chr  "Soaps in Texas" "Whiskey Pens" "The Posh Factory" "Home Made" ...
 $ pledged                 : num  41 2205 8861 100 1026 ...
 $ slug                    : chr  "soaps-in-texas" "whiskey-pens" "the-posh-factory" "home-made" ...
 $ source_url              : chr  "https://www.kickstarter.com/discover/categories/crafts/diy" "https://www.kickstarter.com/discover/categories/crafts" "https://www.kickstarter.com/discover/categories/dance" "https://www.kickstarter.com/discover/categories/crafts/diy" ...
 $ spotlight               : logi  FALSE TRUE TRUE FALSE TRUE FALSE ...
 $ staff_pick              : logi  FALSE FALSE FALSE FALSE TRUE FALSE ...
 $ state                   : chr  "failed" "successful" "successful" "failed" ...
 $ state_changed_at        : chr  "2018-01-04" "2018-07-11" "2015-11-15" "2014-11-07" ...
 $ location_town           : chr  "Edinburg" "Columbia" "Jacksonville" "Detroit" ...
 $ location_state          : chr  "TX" "SC" "FL" "MI" ...
 $ top_category            : chr  "crafts" "crafts" "dance" "crafts" ...
 $ sub_category            : chr  "diy" "woodworking" "spaces" "diy" ...
df$state_changed_at <- as.Date(df$state_changed_at)
my_number <- length(unique(df$top_category))
my_colors <- wes_palette("FantasticFox1", my_number, type = "continuous")
my_colors

1. Identifying Successful Projects

a) Success by Category

There are several ways to identify success of a project:
- State (state): Whether a campaign was successful or not.
- Pledged Amount (pledged)
- Achievement Ratio: The variable achievement_ratio is calculating the percentage of the original monetary goal reached by the actual amount pledged (that is pledged\goal *100).
- Number of backers (backers_count)
- How quickly the goal was reached (difference between launched_at and state_changed_at) for those campaigns that were successful.

Use one or more of these measures to visually summarize which categories were most successful in attracting funding on kickstarter. Briefly summarize your findings.

df$achievement_ratio <- (df$pledged / df$goal) * 100
df$realizing_time <- ifelse(df$state == "successful", as.Date(df$state_changed_at) - as.Date(df$launched_at), NA)

by State (successful or not)

df %>%
  dplyr::select(`top_category`, `state`) %>%
  group_by(`top_category`, `state`) %>%
  summarize(counts = n()) %>%

  ggplot(aes(x = `top_category`, y = counts)) +
  geom_bar(
    aes(color = `state`, fill = `state`),
    stat = "identity", position = position_dodge(0.8),
    width = 0.7) +
  
  labs(
  title = "Kickstarter Projects state by category",
  x = "",
  y = "Number of Projects",
  x.axis = "",
  color = "State of the Project",
  fill = "State of the Project"
  ) +
  
  theme_tufte() + 
  theme(
    plot.title = element_text(size = 13, face = "bold"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    plot.background = element_rect(),
    
    legend.key = element_rect(fill = "white", colour = "white"),
    legend.background = element_rect()
    ) + 
  
  scale_y_continuous(labels = comma) +
  scale_color_manual(values = c("#E17C03", "#B40F20", "#5EA6A9", "#8EBD6B"))+
  scale_fill_manual(values = c("#E17C03", "#B40F20", "#5EA6A9", "#8EBD6B")) +
  scale_x_discrete(guide = guide_axis(n.dodge=3))

At a first glance Music is the most successful while Crafts, Food, Journalism, and Photography seem riskier categories on Kickstarter.

by pledges

df %>%
  dplyr::select(`top_category`, `pledged`, `state_changed_at`, name) %>%
  
  ggplot(aes(x = state_changed_at, y = pledged, color = top_category, size = pledged)) +
  geom_point(na.rm=TRUE) +
  geom_label_repel(data=subset(df, pledged > 7500000), aes(label=(name)), show.legend = FALSE, min.segment.length = 0, seed = 42, box.padding = 0.5, size = 2.5) +
  
  scale_color_manual(values = my_colors) +
  
  labs(
  title = "Kickstarter Projects pledges",
  x = "Years",
  y = "Money pledged",
  x.axis = "",
  color = "Category"
  ) +
  
  theme_tufte() + 
  theme(
    plot.title = element_text(size = 13, face = "bold"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    plot.background = element_rect(),
    
    axis.title.x = element_text(hjust = 0.475),
    
    legend.key = element_rect(fill = "white", colour = "white"),
    legend.background = element_rect()
    ) + 
  
  scale_size(guide="none") +
  
  scale_y_continuous(labels = comma)

Now we know that the four best project ever were in film, games, and technology. Moreover, it seems that, on average, technology receives a lot of pledges.

by Median Achievement Ratio

df %>%
  dplyr::select(`top_category`, `achievement_ratio`) %>%
  group_by(`top_category`) %>%
  summarize(`Median Achievement Ratio` = median(achievement_ratio, na.rm = TRUE)) %>%
  
  ggplot(aes(x = top_category, y = `Median Achievement Ratio`, fill = top_category)) +
  geom_col() +
 
  scale_fill_manual(values = my_colors, aesthetics = "fill") +
  geom_hline(yintercept=100) +
  
  labs(
  title = "Median Achievement per category",
  x = "",
  y = "Percentage (%)",
  x.axis = "",
  fill = "Category"
  ) +
  
  theme_tufte() + 
  theme(
    plot.title = element_text(size = 13, face = "bold"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    plot.background = element_rect(),
   
    
    legend.key = element_rect(fill = "white", colour = "white"),
    legend.background = element_rect()
    ) + 
  
  scale_y_continuous(labels = comma) +
  scale_x_discrete(guide = guide_axis(n.dodge=3))

Median pledges per category

df %>%
  dplyr::select(`top_category`, `pledged`) %>%
  group_by(`top_category`) %>%
  summarize(`Median Pledges` = median(pledged, na.rm = TRUE)) %>%
  
  ggplot(aes(x = top_category, y = `Median Pledges`, fill = top_category)) +
  geom_col() +
 
  scale_fill_manual(values = my_colors, aesthetics = "fill") +
  
  labs(
  title = "Median Pledges per category",
  x = "",
  y = "Dollar ($)",
  x.axis = "",
  fill = "Category"
  ) +
  
  theme_tufte() + 
  theme(
    plot.title = element_text(size = 13, face = "bold"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    plot.background = element_rect(),
   
    
    legend.key = element_rect(fill = "white", colour = "white"),
    legend.background = element_rect()
    ) + 
  
  scale_y_continuous(labels = comma) +
  scale_x_discrete(guide = guide_axis(n.dodge=3))

Success rate per category

success_rate_per_cat <- df %>%
  dplyr::select(`top_category`, `state`) %>%
  filter(`state` == "successful" |`state` == "failed") %>%
  group_by(`top_category`) %>%
  summarize(`Successful Projects` = sum(`state` == "successful", na.rm = TRUE), `Total Projects` = sum(`state` == "successful" | `state` == "failed" , na.rm = TRUE)) %>%
  mutate(`Success Rate` = (`Successful Projects`/ `Total Projects`) *100)

success_rate_per_cat
mean(success_rate_per_cat$`Success Rate`)
[1] 58.30427

BONUS ONLY: b) Success by Location

Now, use the location information to calculate the total number of successful projects by state (if you are ambitious, normalize by population). Also, identify the Top 50 “innovative” cities in the U.S. (by whatever measure you find plausible). Provide a leaflet map showing the most innovative states and cities in the U.S. on a single map based on these information.

df$location <- paste(df$location_town, df$location_state, sep = ", ")
states_pj <- df %>%
  group_by(location_state) %>%
  count(location_state) %>%
  arrange(desc(n))

cities_pg <- df %>%
  group_by(location) %>%
  count(location) %>%
  arrange(desc(n)) %>%
  head(50)

datatable(states_pj, filter = 'top', colnames = c("State", "Projects")) 
datatable(cities_pg,filter = 'top', colnames = c("Top innovative cities", "Projects")) 

If we’d consider Brooklyn as part of NYC, NYC would be the most innovative city. As a measure of a city innovation I simply used the amount of projects that come from there. Now, the whole list of cities is 8,762, which is too much to locate. I need to subset this list. I will keep just the first 100 most innovative cities.

top100_cities <- df %>%
  group_by(location) %>%
  count(location) %>%
  arrange(desc(n)) %>%
  head(100)
GeoCoded <- purrr::map_df(.x = top100_cities$location, .f = ggmap::geocode)
geocoded_df <- dplyr::bind_cols(top100_cities, GeoCoded) %>% 
  dplyr::select(
    lng = lon,
    lat,
    dplyr::everything())

I intalled the “tigris” package. However, I’m not very familiar with it and I found it more simple to use the shape file with the states from https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html .

us <- readOGR(dsn = '/Users/davidev7/Documents/Columbia/Second Semester/Data Visualization/Assignments/HW3/cb_2018_us_state_500k', layer = "cb_2018_us_state_500k")
OGR data source with driver: ESRI Shapefile 
Source: "/Users/davidev7/Documents/Columbia/Second Semester/Data Visualization/Assignments/HW3/cb_2018_us_state_500k", layer: "cb_2018_us_state_500k"
with 56 features
It has 9 fields
Integer64 fields read as strings:  ALAND AWATER 
states_pj$popuptext <- base::paste0("<b>", 
                                 "Number or Projects: ",
                                 "</b><br />",
                                 states_pj$n)
projects_by_state <- merge(us, states_pj, by.x = "STUSPS", by.y = "location_state")
geocoded_df$popuptext <- base::paste0("<b>", 
                                 "Number or Projects: ",
                                 "</b><br />",
                                 geocoded_df$n)
bins <- c(200, 500, 1000, 3000, 5000, 8000, 10000, Inf)
pal <- colorBin("RdYlBu", domain = states_pj$n, bins = bins)
map_innovation <- leaflet(geocoded_df) %>%
  setView(lng = -98.5795, lat = 39.8283, zoom = 2.5) %>%
  addProviderTiles(providers$CartoDB.PositronNoLabels) %>%
  addPolygons(data = projects_by_state, 
            color = "white", 
            weight = 2, 
            smoothFactor = 0.5,
            opacity = 1.0, 
            fillOpacity = 0.9,
            dashArray = "2",
            fillColor = ~pal(n),
            highlightOptions = highlightOptions(color = "red", weight = 2,
                                                bringToFront = TRUE),
            popup = ~popuptext ,
            group = "States",
            label = ~htmlEscape(as.character(NAME))) %>%
  addCircleMarkers(color = "orange",
             label = ~htmlEscape(as.character(location)),
             fill = TRUE,
             lng = ~lng, 
             lat = ~lat,
             popup = ~popuptext,
             group = "Cities") %>%
  addLayersControl(
     baseGroups = c("States", "Cities"),
     options = layersControlOptions(collapsed = FALSE)
  )

map_innovation

2. Writing your success story

Each project contains a blurb – a short description of the project. While not the full description of the project, the short headline is arguably important for inducing interest in the project (and ultimately popularity and success). Let’s analyze the text.

a) Cleaning the Text and Word Cloud

To reduce the time for analysis, select the 1000 most successful projects and a sample of 1000 unsuccessful projects. Use the cleaning functions introduced in lecture (or write your own in addition) to remove unnecessary words (stop words), syntax, punctuation, numbers, white space etc. Note, that many projects use their own unique brand names in upper cases, so try to remove these fully capitalized words as well (since we are aiming to identify common words across descriptions). Create a document-term-matrix.

Provide a word cloud of the most frequent or important words (your choice which frequency measure you choose) among the most successful projects.

removeNumPunct <- function(x){gsub("[^[:alpha:][:space:][-]]*", "", x)}

# my own functions
removeBrand <- function (x) {gsub("\\b[A-Z]+\\b", "", x)}
subDash <- function (x) {gsub("[-]", " ", x)}

# function seen in class
clean_corpus <- function(corpus){
  corpus <- tm_map(corpus, content_transformer(removeBrand))
  corpus <- tm_map(corpus, content_transformer(removeNumPunct))
  corpus <- tm_map(corpus, content_transformer(subDash))
  corpus <- tm_map(corpus, content_transformer(removePunctuation))
  corpus <- tm_map(corpus, content_transformer(tolower))
  corpus <- tm_map(corpus, removeWords, c(stopwords("en")))
    # Optionally, one could add more stop words
  corpus <- tm_map(corpus, stripWhitespace)
  return(corpus)
}

# following the advice of comparing original text and cleaned
# df_cleaned <- clean_corpus(df)

# finally the stemming process
stemCompletion2 <- function(x, dictionary) {
   x <- unlist(strsplit(as.character(x), " "))
   x <- x[x != ""]
   x <- stemCompletion(x, dictionary=dictionary)
   x <- paste(x, sep="", collapse=" ")
   PlainTextDocument(stripWhitespace(x))
}
most_successful_pj <- df %>%
  filter(state == "successful") %>%
  arrange(desc(`pledged`)) %>%
  head(1000)

unsuccessful_pj <- df %>%
  filter(state == "failed") %>%
  sample_n(1000)
df_source_successful <- most_successful_pj %>%
  dplyr::select(`id`, `blurb`) %>%
  rename(doc_id = id, text = blurb)
df_source_un <- unsuccessful_pj %>%
  dplyr::select(`id`, `blurb`) %>%
  rename(doc_id = id, text = blurb)
VCorpus(DataframeSource(df_source_successful))
<<VCorpus>>
Metadata:  corpus specific: 0, document level (indexed): 0
Content:  documents: 1000
s_corpus_raw <- Corpus(VectorSource(df_source_successful$text))
s_corpus_raw$meta$id <- most_successful_pj$id

s_corpus_raw[[1]]$content
[1] "Critical Role's The Legend of Vox Machina reunites your favorite heroes for a professional-quality animated special!"
s_corpus_raw[[2]]$content
[1] "A revolutionary table that evolves over a lifetime. Innovative, yet affordable, with magnetic accessories. Crafted without compromise."
s_corpus_raw[[3]]$content
[1] "Cracking open the last closed platform: the TV.  A beautiful, affordable console -- built on Android, by the creator of Jambox."
u_corpus_raw <- Corpus(VectorSource(df_source_un$text))
u_corpus_raw$meta$id <- unsuccessful_pj$id
s_corpus_cleaned <- clean_corpus(s_corpus_raw)

s_corpus_cleaned[[1]]$content
[1] "critical roles legend vox machina reunites favorite heroes professional quality animated special"
s_corpus_cleaned[[2]]$content
[1] " revolutionary table evolves lifetime innovative yet affordable magnetic accessories crafted without compromise"
s_corpus_cleaned[[3]]$content
[1] "cracking open last closed platform beautiful affordable console built android creator jambox"
s_corpus_stemmed <- tm_map(s_corpus_cleaned, stemDocument)
s_corpus_final <- tm_map(s_corpus_stemmed, stemCompletion2, dictionary = s_corpus_cleaned)
u_corpus_cleaned <- clean_corpus(u_corpus_raw)
u_corpus_stemmed <- tm_map(u_corpus_cleaned, stemDocument)
u_corpus_final <- tm_map(u_corpus_stemmed, stemCompletion2, dictionary = u_corpus_cleaned)
successful_dtm <- DocumentTermMatrix(s_corpus_final)
successful_dtm$dimnames$Docs <- as.character(s_corpus_final$meta$id)
un_dtm <- DocumentTermMatrix(u_corpus_final)
un_dtm$dimnames$Docs <- as.character(u_corpus_final$meta$id)
successful_dtm_tidy <- tidy(successful_dtm)

successful_terms <- successful_dtm_tidy %>%
  group_by(term) %>%
  summarise(n = sum(count)) %>%
  arrange(desc(n)) %>%
  top_n(100)
un_dtm_tidy <- tidy(un_dtm)

unsuccessful_terms <- un_dtm_tidy %>%
  group_by(term) %>%
  summarise(n = sum(count)) %>%
  arrange(desc(n)) %>%
  top_n(100)
my_colors2 <- my_colors[c(8,1,14)]
wordcloud(words = successful_terms$term, 
          freq = as.integer(successful_terms$n),
          scale = c(4, .5),
          min.freq = 0, 
          max.words = 100,
          colors = my_colors2
          )

Since I’ve already done most of the work, I’m interested in a comparison with failed projects.

wordcloud(words = unsuccessful_terms$term, 
          freq = as.integer(unsuccessful_terms$n),
          scale = c(4, .5),
          min.freq = 0, 
          max.words = 100,
          colors = my_colors
          )

Apparently a cry for help doesn’t help! New appears to be equally frequent in both. While first is a signature for successful projects!

b) Success in words

Provide a pyramid plot to show how the words between successful and unsuccessful projects differ in frequency. A selection of 10 - 20 top words is sufficient here.

all_successful_terms <- successful_dtm_tidy %>%
  group_by(term) %>%
  summarise(n = sum(count)) %>%
  arrange(desc(n))

all_unsuccessful_terms <- un_dtm_tidy %>%
  group_by(term) %>%
  summarise(n = sum(count)) %>%
  arrange(desc(n))
terms <- merge(all_successful_terms, all_unsuccessful_terms, by = "term")
terms_for_plot <- terms %>% 
  filter(n.x > 30 | n.y >30)
terms_for_plot <- terms_for_plot[c(1, 7, 16, 19, 24, 27, 20, 29, 33, 35, 36, 50, 52, 53, 41, 42, 48, 12, 9, 3),]
scol<- my_colors[6]
ucol<- my_colors[14]
pyramid.plot(terms_for_plot$n.x, terms_for_plot$n.y, labels=terms_for_plot$term,
  main="Words frequency pyramid plot",
  top.labels = c("Successful", "Term", "Unsuccessful"),
  lxcol=scol,rxcol=ucol,
  laxlab = NULL,
             raxlab = NULL,
             unit = NULL,
             labelcex=0.5,
  gap=23,show.values=TRUE)
101 101 
[1] 5.1 4.1 2.1 2.1

It’s a very interesting graph from a psychological perspective. The more the words are “needy” the more is likely the project is going to fail. The same can be said for those projects in which things are not done yet. Words like “will” “make” are associated with failure.

c) Simplicity as a virtue

These blurbs are short in length (max. 150 characters) but let’s see whether brevity and simplicity still matters. Calculate a readability measure (Flesh Reading Ease, Flesh Kincaid or any other comparable measure) for the texts. Visualize the relationship between the readability measure and one of the measures of success. Briefly comment on your finding.

s_pj <- most_successful_pj %>%
  dplyr::select(`blurb`, `state`, `goal`, `achievement_ratio`) %>%
  filter(`goal` > 1000)

u_pj <- unsuccessful_pj %>%
  dplyr::select(`blurb`, `state`, `goal`, `achievement_ratio`) %>%
  filter(`goal` > 1000)

df_merged <- rbind(s_pj, u_pj)
readability_score <- textstat_readability(as.character(df_merged$blurb), 
        measure = c('Flesch','Flesch.Kincaid',
                  'meanSentenceLength','meanWordSyllables'))

readability_score[colnames(df_merged)] <- df_merged
readability_score %>%
  filter(log(achievement_ratio) > -10) %>%
  
  ggplot(aes(x = log(achievement_ratio), y = Flesch.Kincaid, color = state)) +
  geom_point() +
  geom_smooth(color = my_colors[8]) +
  
  scale_color_manual(values = c(ucol, scol)) +
  
  labs(
    title = "Relationship between Readability and Success", 
    x = "log Achievement Ratio", 
    y = "Flesch-Kincaid Grade Level",
    color = "State"
    ) +
  
  
  theme_tufte() + 
  theme(
    plot.title = element_text(size = 13, face = "bold"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    plot.background = element_rect(),
    
    axis.title.x = element_text(hjust = 0.475),
    
    legend.key = element_rect(fill = "white", colour = "white"),
    legend.background = element_rect()
    )

There is a slightly, but negligible, positive relationship between having a description with an higher Flesch-Kincaid score and having an higher Achievement Ratio (Money pledged / Goal, when Goal > 1000). I filtered for Goal > 1000 because many projects set very low (unrealistic) goals. The Flesch-Kincaid scale measures how easily readable a text is. The higher the score the easier the text is easy to read.

3. Sentiment

Now, let’s check whether the use of positive / negative words or specific emotions helps a project to be successful.

a) Stay positive

Calculate the tone of each text based on the positive and negative words that are being used. You can rely on the Hu & Liu dictionary provided in lecture or use the Bing dictionary contained in the tidytext package (tidytext::sentiments). Visualize the relationship between tone of the document and success. Briefly comment.

After consulting https://www.tidytextmining.com/sentiment.html, I selected the “nrc” dictionary from Saif Mohammad and Peter Turney as it’s the one with more words.

dataframe1 <- data.frame(text=sapply(s_corpus_cleaned, identity), 
    stringsAsFactors=F)
dataframe1 <- dataframe1 %>% 
  mutate(index = row_number())

df1.1 <- most_successful_pj %>%
  dplyr::select(`state`, `goal`, `achievement_ratio`) %>%
  mutate(index = row_number())

dataframe1.2 <- left_join(dataframe1, df1.1, by = "index")
dataframe2 <- data.frame(text=sapply(u_corpus_cleaned, identity), 
    stringsAsFactors=F)
dataframe2 <- dataframe2 %>% 
  mutate(index = row_number())

df2.1 <- unsuccessful_pj %>%
  dplyr::select(`state`, `goal`, `achievement_ratio`) %>%
  mutate(index = row_number())

dataframe2.2 <- left_join(dataframe2, df2.1, by = "index")
df_merged_cleaned <- rbind(dataframe1.2, dataframe2.2)
library(tidytext)
library(syuzhet)

polarity_score <- get_sentiment(df_merged_cleaned$text, method="nrc")
log(100)
[1] 4.60517

When Achievement Rate = 100 the project reached its goal.

df_merged_cleaned %>% 
  filter(goal > 1000) %>%
  filter(log(achievement_ratio) > -8) %>%
 
  ggplot(aes(x = sentiment, y = log(achievement_ratio), color = state)) + 
  geom_point() +
  geom_smooth(color = my_colors[8]) +
  
  geom_hline(yintercept = 4.60517) +
  geom_label(
    label="Goal reached", 
    x=6.35,
    y=6,
    label.padding = unit(0.45, "lines"),
    label.size = 0.15,
    color = "black",
    fill="honeydew2"
  ) +
  
  scale_color_manual(values = c(ucol, scol)) +
  
  labs(
    title = "Relationship between Text Sentiment and Success", 
    x = "Text Sentiment", 
    y = "log Achievement Ratio",
    color = "State"
    ) +
  
  
  theme_tufte() + 
  theme(
    plot.title = element_text(size = 13, face = "bold"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    plot.background = element_rect(),
    
    axis.title.x = element_text(hjust = 0.475),
    
    legend.key = element_rect(fill = "white", colour = "white"),
    legend.background = element_rect()
    )

This is a weird result. Apparently, once the text of a project goes beyond a slightly positive sentiment, its success chances decrease. However, it might make sense! Extremely positive texts might be suspicious. Projects might appear too good to be true.

b) Positive vs negative

Segregate all 2,000 blurbs into positive and negative texts based on their polarity score calculated in step (a). Now, collapse the positive and negative texts into two larger documents. Create a document-term-matrix based on this collapsed set of two documents. Generate a comparison cloud showing the most-frequent positive and negative words.

df_merged_cleaned$polarity <- ifelse(df_merged_cleaned$sentiment < 0, "Negative", ifelse(df_merged_cleaned$sentiment >= 0, "Positive", NA))
df_positive <- df_merged_cleaned %>%
  filter(polarity == "Positive")

df_negative <- df_merged_cleaned %>%
  filter(polarity == "Negative")
df_3b1 <- unnest_tokens(df_merged_cleaned, output = word, input = text) 

df_3b <- df_3b1 %>%
  group_by(polarity, word) %>%
  summarise(count = n()) %>%
  cast_tdm(word, polarity, count)

matrix3b <- as.matrix(df_3b)
set.seed(2105)
comparison.cloud(matrix3b, colors = c(ucol, scol), 
                 title.size= 1.3,
                 max.words = 100,
                 random.order = FALSE)

c) Get in their mind

Now, use the NRC Word-Emotion Association Lexicon in the tidytext package to identify a larger set of emotions (anger, anticipation, disgust, fear, joy, sadness, surprise, trust). Again, visualize the relationship between the use of words from these categories and success. What is your finding?

sentiments <- get_sentiments("nrc")
df_sentiments <-  inner_join(df_3b1, sentiments, by = "word")
df_sentiments %>%
  
  ggplot(aes(x = sentiment.y, y = achievement_ratio, color = state)) + 
  geom_point() +
  
  scale_color_manual(values = c(ucol, scol)) +
  
  labs(
    title = "Relationship between Sentiments and Success", 
    x = "Sentiments", 
    y = "Achievement Ratio",
    color = "State"
    ) +
  
  
  theme_tufte() + 
  theme(
    plot.title = element_text(size = 13, face = "bold"),
    plot.caption = element_text(hjust = 0, face = "italic"),
    plot.background = element_rect(),
    
    legend.key = element_rect(fill = "white", colour = "white"),
    legend.background = element_rect()
    )

The most successful projects have words that induce positive emotions like Anticipation, Joy, Positive, Surprise, and Trust. There is also a successful project that has Anger and Negative. However, none of the most successful has Disgust or Fear or Sadness. Moreover, Positive and Trust seem to be the two with the highest correlation with the Achievement Ratio.

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAzOiBLaWNrc3RhcnRlciBQcm9qZWN0cycKYXV0aG9yOiAiRGF2aWRlIFZhY2NhcmkiCmRhdGU6ICIzLzMxLzIwMjEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGVycm9yID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IG5vcm1hbGl6ZVBhdGgoIi9Vc2Vycy9kYXZpZGV2Ny9Eb2N1bWVudHMvQ29sdW1iaWEvU2Vjb25kIFNlbWVzdGVyL0RhdGEgVmlzdWFsaXphdGlvbi9Bc3NpZ25tZW50cy9IVzMiKSkKYGBgCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZ2RhbCkKbGlicmFyeSh0bWFwKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShzZikKbGlicmFyeShnZ21hcCkKbGlicmFyeShEVCkKbGlicmFyeShnZ21hcCkKbGlicmFyeShodG1sdG9vbHMpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShxdWFudGVkYSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0bSkKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkod2VzYW5kZXJzb24pCmxpYnJhcnkocWRhcCkKbGlicmFyeShTbm93YmFsbEMpCmxpYnJhcnkocGxvdHJpeCkKbGlicmFyeShtYWdyaXR0cikKYGBgCgpgYGB7cn0KZ2dtYXA6OnJlZ2lzdGVyX2dvb2dsZShrZXkgPSAiQUl6YVN5QjEtTWppWEVJcmdkVDNGYmZsTUxjOEVVYVFYVkczWFZZIikKYGBgCgpLaWNrc3RhcnRlciBQcm9qZWN0cwo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKYGBge3J9CmRmIDwtIHJlYWQuY3N2KCIvVXNlcnMvZGF2aWRldjcvRG9jdW1lbnRzL0NvbHVtYmlhL1NlY29uZCBTZW1lc3Rlci9EYXRhIFZpc3VhbGl6YXRpb24vY291cnNlX2NvbnRlbnQtbWFpbi9FeGVyY2lzZXMvMDlfa2lja3N0YXJ0ZXIva2lja3N0YXJ0ZXJfcHJvamVjdHNfMjAyMS0wMy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpzdHIoZGYpCmBgYAoKYGBge3J9CmRmJHN0YXRlX2NoYW5nZWRfYXQgPC0gYXMuRGF0ZShkZiRzdGF0ZV9jaGFuZ2VkX2F0KQpgYGAKCmBgYHtyfQpteV9udW1iZXIgPC0gbGVuZ3RoKHVuaXF1ZShkZiR0b3BfY2F0ZWdvcnkpKQpteV9jb2xvcnMgPC0gd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiLCBteV9udW1iZXIsIHR5cGUgPSAiY29udGludW91cyIpCm15X2NvbG9ycwpgYGAKCiMjIDEuIElkZW50aWZ5aW5nIFN1Y2Nlc3NmdWwgUHJvamVjdHMKCiMjIyBhKSBTdWNjZXNzIGJ5IENhdGVnb3J5CgpUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIGlkZW50aWZ5IHN1Y2Nlc3Mgb2YgYSBwcm9qZWN0OiAgCiAgLSBTdGF0ZSAoYHN0YXRlYCk6IFdoZXRoZXIgYSBjYW1wYWlnbiB3YXMgc3VjY2Vzc2Z1bCBvciBub3QuICAgCiAgLSBQbGVkZ2VkIEFtb3VudCAoYHBsZWRnZWRgKSAgIAogIC0gQWNoaWV2ZW1lbnQgUmF0aW86IFRoZSB2YXJpYWJsZSBgYWNoaWV2ZW1lbnRfcmF0aW9gIGlzIGNhbGN1bGF0aW5nIHRoZSBwZXJjZW50YWdlIG9mIHRoZSBvcmlnaW5hbCBtb25ldGFyeSBgZ29hbGAgcmVhY2hlZCBieSB0aGUgYWN0dWFsIGFtb3VudCBgcGxlZGdlZGAgKHRoYXQgaXMgYHBsZWRnZWRgXFxgZ29hbGAgKjEwMCkuICAgIAogIC0gTnVtYmVyIG9mIGJhY2tlcnMgKGBiYWNrZXJzX2NvdW50YCkgIAogIC0gSG93IHF1aWNrbHkgdGhlIGdvYWwgd2FzIHJlYWNoZWQgKGRpZmZlcmVuY2UgYmV0d2VlbiBgbGF1bmNoZWRfYXRgIGFuZCBgc3RhdGVfY2hhbmdlZF9hdGApIGZvciB0aG9zZSBjYW1wYWlnbnMgdGhhdCB3ZXJlIHN1Y2Nlc3NmdWwuICAKClVzZSBvbmUgb3IgbW9yZSBvZiB0aGVzZSBtZWFzdXJlcyB0byB2aXN1YWxseSBzdW1tYXJpemUgd2hpY2ggY2F0ZWdvcmllcyB3ZXJlIG1vc3Qgc3VjY2Vzc2Z1bCBpbiBhdHRyYWN0aW5nIGZ1bmRpbmcgb24ga2lja3N0YXJ0ZXIuIEJyaWVmbHkgc3VtbWFyaXplIHlvdXIgZmluZGluZ3MuCgpgYGB7cn0KZGYkYWNoaWV2ZW1lbnRfcmF0aW8gPC0gKGRmJHBsZWRnZWQgLyBkZiRnb2FsKSAqIDEwMApkZiRyZWFsaXppbmdfdGltZSA8LSBpZmVsc2UoZGYkc3RhdGUgPT0gInN1Y2Nlc3NmdWwiLCBhcy5EYXRlKGRmJHN0YXRlX2NoYW5nZWRfYXQpIC0gYXMuRGF0ZShkZiRsYXVuY2hlZF9hdCksIE5BKQpgYGAKCmBgYHtyfQpoZWFkKGRmKQpgYGAKCiMjIyMgYnkgU3RhdGUgKHN1Y2Nlc3NmdWwgb3Igbm90KQoKYGBge3J9CmRmICU+JQogIGRwbHlyOjpzZWxlY3QoYHRvcF9jYXRlZ29yeWAsIGBzdGF0ZWApICU+JQogIGdyb3VwX2J5KGB0b3BfY2F0ZWdvcnlgLCBgc3RhdGVgKSAlPiUKICBzdW1tYXJpemUoY291bnRzID0gbigpKSAlPiUKCiAgZ2dwbG90KGFlcyh4ID0gYHRvcF9jYXRlZ29yeWAsIHkgPSBjb3VudHMpKSArCiAgZ2VvbV9iYXIoCiAgICBhZXMoY29sb3IgPSBgc3RhdGVgLCBmaWxsID0gYHN0YXRlYCksCiAgICBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjgpLAogICAgd2lkdGggPSAwLjcpICsKICAKICBsYWJzKAogIHRpdGxlID0gIktpY2tzdGFydGVyIFByb2plY3RzIHN0YXRlIGJ5IGNhdGVnb3J5IiwKICB4ID0gIiIsCiAgeSA9ICJOdW1iZXIgb2YgUHJvamVjdHMiLAogIHguYXhpcyA9ICIiLAogIGNvbG9yID0gIlN0YXRlIG9mIHRoZSBQcm9qZWN0IiwKICBmaWxsID0gIlN0YXRlIG9mIHRoZSBQcm9qZWN0IgogICkgKwogIAogIHRoZW1lX3R1ZnRlKCkgKyAKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLCBmYWNlID0gImJvbGQiKSwKICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIGZhY2UgPSAiaXRhbGljIiksCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoKSwKICAgIAogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gIndoaXRlIiksCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpCiAgICApICsgCiAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNFMTdDMDMiLCAiI0I0MEYyMCIsICIjNUVBNkE5IiwgIiM4RUJENkIiKSkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0UxN0MwMyIsICIjQjQwRjIwIiwgIiM1RUE2QTkiLCAiIzhFQkQ2QiIpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2F4aXMobi5kb2RnZT0zKSkKYGBgCgpBdCBhIGZpcnN0IGdsYW5jZSBNdXNpYyBpcyB0aGUgbW9zdCBzdWNjZXNzZnVsIHdoaWxlIENyYWZ0cywgRm9vZCwgSm91cm5hbGlzbSwgYW5kIFBob3RvZ3JhcGh5IHNlZW0gcmlza2llciBjYXRlZ29yaWVzIG9uIEtpY2tzdGFydGVyLgoKIyMjIyBieSBwbGVkZ2VzCgpgYGB7cn0KZGYgJT4lCiAgZHBseXI6OnNlbGVjdChgdG9wX2NhdGVnb3J5YCwgYHBsZWRnZWRgLCBgc3RhdGVfY2hhbmdlZF9hdGAsIG5hbWUpICU+JQogIAogIGdncGxvdChhZXMoeCA9IHN0YXRlX2NoYW5nZWRfYXQsIHkgPSBwbGVkZ2VkLCBjb2xvciA9IHRvcF9jYXRlZ29yeSwgc2l6ZSA9IHBsZWRnZWQpKSArCiAgZ2VvbV9wb2ludChuYS5ybT1UUlVFKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhPXN1YnNldChkZiwgcGxlZGdlZCA+IDc1MDAwMDApLCBhZXMobGFiZWw9KG5hbWUpKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbWluLnNlZ21lbnQubGVuZ3RoID0gMCwgc2VlZCA9IDQyLCBib3gucGFkZGluZyA9IDAuNSwgc2l6ZSA9IDIuNSkgKwogIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMpICsKICAKICBsYWJzKAogIHRpdGxlID0gIktpY2tzdGFydGVyIFByb2plY3RzIHBsZWRnZXMiLAogIHggPSAiWWVhcnMiLAogIHkgPSAiTW9uZXkgcGxlZGdlZCIsCiAgeC5heGlzID0gIiIsCiAgY29sb3IgPSAiQ2F0ZWdvcnkiCiAgKSArCiAgCiAgdGhlbWVfdHVmdGUoKSArIAogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCwgZmFjZSA9ICJpdGFsaWMiKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpLAogICAgCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjQ3NSksCiAgICAKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJ3aGl0ZSIpLAogICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoKQogICAgKSArIAogIAogIHNjYWxlX3NpemUoZ3VpZGU9Im5vbmUiKSArCiAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKQpgYGAKCk5vdyB3ZSBrbm93IHRoYXQgdGhlIGZvdXIgYmVzdCBwcm9qZWN0IGV2ZXIgd2VyZSBpbiBmaWxtLCBnYW1lcywgYW5kIHRlY2hub2xvZ3kuIE1vcmVvdmVyLCBpdCBzZWVtcyB0aGF0LCBvbiBhdmVyYWdlLCB0ZWNobm9sb2d5IHJlY2VpdmVzIGEgbG90IG9mIHBsZWRnZXMuCgojIyMjIGJ5IE1lZGlhbiBBY2hpZXZlbWVudCBSYXRpbwoKYGBge3J9CmRmICU+JQogIGRwbHlyOjpzZWxlY3QoYHRvcF9jYXRlZ29yeWAsIGBhY2hpZXZlbWVudF9yYXRpb2ApICU+JQogIGdyb3VwX2J5KGB0b3BfY2F0ZWdvcnlgKSAlPiUKICBzdW1tYXJpemUoYE1lZGlhbiBBY2hpZXZlbWVudCBSYXRpb2AgPSBtZWRpYW4oYWNoaWV2ZW1lbnRfcmF0aW8sIG5hLnJtID0gVFJVRSkpICU+JQogIAogIGdncGxvdChhZXMoeCA9IHRvcF9jYXRlZ29yeSwgeSA9IGBNZWRpYW4gQWNoaWV2ZW1lbnQgUmF0aW9gLCBmaWxsID0gdG9wX2NhdGVnb3J5KSkgKwogIGdlb21fY29sKCkgKwogCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbXlfY29sb3JzLCBhZXN0aGV0aWNzID0gImZpbGwiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCkgKwogIAogIGxhYnMoCiAgdGl0bGUgPSAiTWVkaWFuIEFjaGlldmVtZW50IHBlciBjYXRlZ29yeSIsCiAgeCA9ICIiLAogIHkgPSAiUGVyY2VudGFnZSAoJSkiLAogIHguYXhpcyA9ICIiLAogIGZpbGwgPSAiQ2F0ZWdvcnkiCiAgKSArCiAgCiAgdGhlbWVfdHVmdGUoKSArIAogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCwgZmFjZSA9ICJpdGFsaWMiKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpLAogICAKICAgIAogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gIndoaXRlIiksCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpCiAgICApICsgCiAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2F4aXMobi5kb2RnZT0zKSkKYGBgCgojIyMjIE1lZGlhbiBwbGVkZ2VzIHBlciBjYXRlZ29yeQoKYGBge3J9CmRmICU+JQogIGRwbHlyOjpzZWxlY3QoYHRvcF9jYXRlZ29yeWAsIGBwbGVkZ2VkYCkgJT4lCiAgZ3JvdXBfYnkoYHRvcF9jYXRlZ29yeWApICU+JQogIHN1bW1hcml6ZShgTWVkaWFuIFBsZWRnZXNgID0gbWVkaWFuKHBsZWRnZWQsIG5hLnJtID0gVFJVRSkpICU+JQogIAogIGdncGxvdChhZXMoeCA9IHRvcF9jYXRlZ29yeSwgeSA9IGBNZWRpYW4gUGxlZGdlc2AsIGZpbGwgPSB0b3BfY2F0ZWdvcnkpKSArCiAgZ2VvbV9jb2woKSArCiAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteV9jb2xvcnMsIGFlc3RoZXRpY3MgPSAiZmlsbCIpICsKICAKICBsYWJzKAogIHRpdGxlID0gIk1lZGlhbiBQbGVkZ2VzIHBlciBjYXRlZ29yeSIsCiAgeCA9ICIiLAogIHkgPSAiRG9sbGFyICgkKSIsCiAgeC5heGlzID0gIiIsCiAgZmlsbCA9ICJDYXRlZ29yeSIKICApICsKICAKICB0aGVtZV90dWZ0ZSgpICsgCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgZmFjZSA9ICJib2xkIiksCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBmYWNlID0gIml0YWxpYyIpLAogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCksCiAgIAogICAgCiAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAid2hpdGUiKSwKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCkKICAgICkgKyAKICAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBzY2FsZV94X2Rpc2NyZXRlKGd1aWRlID0gZ3VpZGVfYXhpcyhuLmRvZGdlPTMpKQpgYGAKCiMjIyMgU3VjY2VzcyByYXRlIHBlciBjYXRlZ29yeQoKYGBge3J9CnN1Y2Nlc3NfcmF0ZV9wZXJfY2F0IDwtIGRmICU+JQogIGRwbHlyOjpzZWxlY3QoYHRvcF9jYXRlZ29yeWAsIGBzdGF0ZWApICU+JQogIGZpbHRlcihgc3RhdGVgID09ICJzdWNjZXNzZnVsIiB8YHN0YXRlYCA9PSAiZmFpbGVkIikgJT4lCiAgZ3JvdXBfYnkoYHRvcF9jYXRlZ29yeWApICU+JQogIHN1bW1hcml6ZShgU3VjY2Vzc2Z1bCBQcm9qZWN0c2AgPSBzdW0oYHN0YXRlYCA9PSAic3VjY2Vzc2Z1bCIsIG5hLnJtID0gVFJVRSksIGBUb3RhbCBQcm9qZWN0c2AgPSBzdW0oYHN0YXRlYCA9PSAic3VjY2Vzc2Z1bCIgfCBgc3RhdGVgID09ICJmYWlsZWQiICwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGBTdWNjZXNzIFJhdGVgID0gKGBTdWNjZXNzZnVsIFByb2plY3RzYC8gYFRvdGFsIFByb2plY3RzYCkgKjEwMCkKCnN1Y2Nlc3NfcmF0ZV9wZXJfY2F0CmBgYAoKYGBge3J9Cm1lYW4oc3VjY2Vzc19yYXRlX3Blcl9jYXQkYFN1Y2Nlc3MgUmF0ZWApCmBgYAoKCmBgYHtyfQpzdWNjZXNzX3JhdGVfcGVyX2NhdCAlPiUKICAKIGdncGxvdChhZXMoeCA9IHRvcF9jYXRlZ29yeSwgeSA9IGBTdWNjZXNzIFJhdGVgLCBmaWxsID0gdG9wX2NhdGVnb3J5KSkgKwogIGdlb21fY29sKCkgKwogCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbXlfY29sb3JzLCBhZXN0aGV0aWNzID0gImZpbGwiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNTguMzA0MjcpICsKICBnZW9tX2xhYmVsKAogICAgbGFiZWw9IkF2ZXJhZ2UgU3VjY2VzcyBSYXRlIiwgCiAgICB4PTcuNSwKICAgIHk9NTIsCiAgICBsYWJlbC5wYWRkaW5nID0gdW5pdCgwLjU1LCAibGluZXMiKSwKICAgIGxhYmVsLnNpemUgPSAwLjE1LAogICAgY29sb3IgPSAiYmxhY2siLAogICAgZmlsbD0iaG9uZXlkZXcyIgogICkgKwogIAogIGxhYnMoCiAgdGl0bGUgPSAiU3VjY2VzcyBSYXRlIHBlciBjYXRlZ29yeSIsCiAgeCA9ICIiLAogIHkgPSAiUGVyY2VudGFnZSAoJSkiLAogIHguYXhpcyA9ICIiLAogIGZpbGwgPSAiQ2F0ZWdvcnkiCiAgKSArCiAgCiAgdGhlbWVfdHVmdGUoKSArIAogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCwgZmFjZSA9ICJpdGFsaWMiKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpLAogICAKICAgIAogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gIndoaXRlIiksCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpCiAgICApICsgCiAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2F4aXMobi5kb2RnZT0zKSkKYGBgCgojIyMgKipCT05VUyBPTkxZOioqIGIpIFN1Y2Nlc3MgYnkgTG9jYXRpb24KCk5vdywgdXNlIHRoZSBsb2NhdGlvbiBpbmZvcm1hdGlvbiB0byBjYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBzdWNjZXNzZnVsIHByb2plY3RzIGJ5IHN0YXRlIChpZiB5b3UgYXJlIGFtYml0aW91cywgbm9ybWFsaXplIGJ5IHBvcHVsYXRpb24pLiBBbHNvLCBpZGVudGlmeSB0aGUgVG9wIDUwICJpbm5vdmF0aXZlIiBjaXRpZXMgaW4gdGhlIFUuUy4gKGJ5IHdoYXRldmVyIG1lYXN1cmUgeW91IGZpbmQgcGxhdXNpYmxlKS4gUHJvdmlkZSBhIGxlYWZsZXQgbWFwIHNob3dpbmcgdGhlIG1vc3QgaW5ub3ZhdGl2ZSBzdGF0ZXMgYW5kIGNpdGllcyBpbiB0aGUgVS5TLiBvbiBhIHNpbmdsZSBtYXAgYmFzZWQgb24gdGhlc2UgaW5mb3JtYXRpb24uCgpgYGB7cn0KZGYkbG9jYXRpb24gPC0gcGFzdGUoZGYkbG9jYXRpb25fdG93biwgZGYkbG9jYXRpb25fc3RhdGUsIHNlcCA9ICIsICIpCmBgYAoKYGBge3J9CnN0YXRlc19waiA8LSBkZiAlPiUKICBncm91cF9ieShsb2NhdGlvbl9zdGF0ZSkgJT4lCiAgY291bnQobG9jYXRpb25fc3RhdGUpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKCmNpdGllc19wZyA8LSBkZiAlPiUKICBncm91cF9ieShsb2NhdGlvbikgJT4lCiAgY291bnQobG9jYXRpb24pICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgaGVhZCg1MCkKCmRhdGF0YWJsZShzdGF0ZXNfcGosIGZpbHRlciA9ICd0b3AnLCBjb2xuYW1lcyA9IGMoIlN0YXRlIiwgIlByb2plY3RzIikpIApkYXRhdGFibGUoY2l0aWVzX3BnLGZpbHRlciA9ICd0b3AnLCBjb2xuYW1lcyA9IGMoIlRvcCBpbm5vdmF0aXZlIGNpdGllcyIsICJQcm9qZWN0cyIpKSAKYGBgCgpJZiB3ZSdkIGNvbnNpZGVyIEJyb29rbHluIGFzIHBhcnQgb2YgTllDLCBOWUMgd291bGQgYmUgdGhlIG1vc3QgaW5ub3ZhdGl2ZSBjaXR5LiBBcyBhIG1lYXN1cmUgb2YgYSBjaXR5IGlubm92YXRpb24gSSBzaW1wbHkgdXNlZCB0aGUgYW1vdW50IG9mIHByb2plY3RzIHRoYXQgY29tZSBmcm9tIHRoZXJlLgpOb3csIHRoZSB3aG9sZSBsaXN0IG9mIGNpdGllcyBpcyA4LDc2Miwgd2hpY2ggaXMgdG9vIG11Y2ggdG8gbG9jYXRlLiBJIG5lZWQgdG8gc3Vic2V0IHRoaXMgbGlzdC4gSSB3aWxsIGtlZXAganVzdCB0aGUgZmlyc3QgMTAwIG1vc3QgaW5ub3ZhdGl2ZSBjaXRpZXMuCgpgYGB7cn0KdG9wMTAwX2NpdGllcyA8LSBkZiAlPiUKICBncm91cF9ieShsb2NhdGlvbikgJT4lCiAgY291bnQobG9jYXRpb24pICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgaGVhZCgxMDApCmBgYAoKCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30KR2VvQ29kZWQgPC0gcHVycnI6Om1hcF9kZigueCA9IHRvcDEwMF9jaXRpZXMkbG9jYXRpb24sIC5mID0gZ2dtYXA6Omdlb2NvZGUpCmBgYAoKYGBge3J9Cmdlb2NvZGVkX2RmIDwtIGRwbHlyOjpiaW5kX2NvbHModG9wMTAwX2NpdGllcywgR2VvQ29kZWQpICU+JSAKICBkcGx5cjo6c2VsZWN0KAogICAgbG5nID0gbG9uLAogICAgbGF0LAogICAgZHBseXI6OmV2ZXJ5dGhpbmcoKSkKYGBgCgpJIGludGFsbGVkIHRoZSAidGlncmlzIiBwYWNrYWdlLiBIb3dldmVyLCBJJ20gbm90IHZlcnkgZmFtaWxpYXIgd2l0aCBpdCBhbmQgSSBmb3VuZCBpdCBtb3JlIHNpbXBsZSB0byB1c2UgdGhlIHNoYXBlIGZpbGUgd2l0aCB0aGUgc3RhdGVzIGZyb20gaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9nZW9ncmFwaGllcy9tYXBwaW5nLWZpbGVzL3RpbWUtc2VyaWVzL2dlby9jYXJ0by1ib3VuZGFyeS1maWxlLmh0bWwgLgoKYGBge3J9CnVzIDwtIHJlYWRPR1IoZHNuID0gJy9Vc2Vycy9kYXZpZGV2Ny9Eb2N1bWVudHMvQ29sdW1iaWEvU2Vjb25kIFNlbWVzdGVyL0RhdGEgVmlzdWFsaXphdGlvbi9Bc3NpZ25tZW50cy9IVzMvY2JfMjAxOF91c19zdGF0ZV81MDBrJywgbGF5ZXIgPSAiY2JfMjAxOF91c19zdGF0ZV81MDBrIikKYGBgCmBgYHtyfQpzdGF0ZXNfcGokcG9wdXB0ZXh0IDwtIGJhc2U6OnBhc3RlMCgiPGI+IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOdW1iZXIgb3IgUHJvamVjdHM6ICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8L2I+PGJyIC8+IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdGVzX3BqJG4pCnByb2plY3RzX2J5X3N0YXRlIDwtIG1lcmdlKHVzLCBzdGF0ZXNfcGosIGJ5LnggPSAiU1RVU1BTIiwgYnkueSA9ICJsb2NhdGlvbl9zdGF0ZSIpCmBgYAoKYGBge3J9Cmdlb2NvZGVkX2RmJHBvcHVwdGV4dCA8LSBiYXNlOjpwYXN0ZTAoIjxiPiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTnVtYmVyIG9yIFByb2plY3RzOiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPC9iPjxiciAvPiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb2NvZGVkX2RmJG4pCmBgYAoKYGBge3J9CmJpbnMgPC0gYygyMDAsIDUwMCwgMTAwMCwgMzAwMCwgNTAwMCwgODAwMCwgMTAwMDAsIEluZikKcGFsIDwtIGNvbG9yQmluKCJSZFlsQnUiLCBkb21haW4gPSBzdGF0ZXNfcGokbiwgYmlucyA9IGJpbnMpCmBgYAoKYGBge3J9Cm1hcF9pbm5vdmF0aW9uIDwtIGxlYWZsZXQoZ2VvY29kZWRfZGYpICU+JQogIHNldFZpZXcobG5nID0gLTk4LjU3OTUsIGxhdCA9IDM5LjgyODMsIHpvb20gPSAyLjUpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb25Ob0xhYmVscykgJT4lCiAgYWRkUG9seWdvbnMoZGF0YSA9IHByb2plY3RzX2J5X3N0YXRlLCAKICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCAKICAgICAgICAgICAgd2VpZ2h0ID0gMiwgCiAgICAgICAgICAgIHNtb290aEZhY3RvciA9IDAuNSwKICAgICAgICAgICAgb3BhY2l0eSA9IDEuMCwgCiAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC45LAogICAgICAgICAgICBkYXNoQXJyYXkgPSAiMiIsCiAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wYWwobiksCiAgICAgICAgICAgIGhpZ2hsaWdodE9wdGlvbnMgPSBoaWdobGlnaHRPcHRpb25zKGNvbG9yID0gInJlZCIsIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyaW5nVG9Gcm9udCA9IFRSVUUpLAogICAgICAgICAgICBwb3B1cCA9IH5wb3B1cHRleHQgLAogICAgICAgICAgICBncm91cCA9ICJTdGF0ZXMiLAogICAgICAgICAgICBsYWJlbCA9IH5odG1sRXNjYXBlKGFzLmNoYXJhY3RlcihOQU1FKSkpICU+JQogIGFkZENpcmNsZU1hcmtlcnMoY29sb3IgPSAib3JhbmdlIiwKICAgICAgICAgICAgIGxhYmVsID0gfmh0bWxFc2NhcGUoYXMuY2hhcmFjdGVyKGxvY2F0aW9uKSksCiAgICAgICAgICAgICBmaWxsID0gVFJVRSwKICAgICAgICAgICAgIGxuZyA9IH5sbmcsIAogICAgICAgICAgICAgbGF0ID0gfmxhdCwKICAgICAgICAgICAgIHBvcHVwID0gfnBvcHVwdGV4dCwKICAgICAgICAgICAgIGdyb3VwID0gIkNpdGllcyIpICU+JQogIGFkZExheWVyc0NvbnRyb2woCiAgICAgYmFzZUdyb3VwcyA9IGMoIlN0YXRlcyIsICJDaXRpZXMiKSwKICAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpCiAgKQoKbWFwX2lubm92YXRpb24KYGBgCgojIyAyLiBXcml0aW5nIHlvdXIgc3VjY2VzcyBzdG9yeQoKRWFjaCBwcm9qZWN0IGNvbnRhaW5zIGEgYGJsdXJiYCAtLSBhIHNob3J0IGRlc2NyaXB0aW9uIG9mIHRoZSBwcm9qZWN0LiBXaGlsZSBub3QgdGhlIGZ1bGwgZGVzY3JpcHRpb24gb2YgdGhlIHByb2plY3QsIHRoZSBzaG9ydCBoZWFkbGluZSBpcyBhcmd1YWJseSBpbXBvcnRhbnQgZm9yIGluZHVjaW5nIGludGVyZXN0IGluIHRoZSBwcm9qZWN0IChhbmQgdWx0aW1hdGVseSBwb3B1bGFyaXR5IGFuZCBzdWNjZXNzKS4gTGV0J3MgYW5hbHl6ZSB0aGUgdGV4dC4KCiMjIyBhKSBDbGVhbmluZyB0aGUgVGV4dCBhbmQgV29yZCBDbG91ZAoKVG8gcmVkdWNlIHRoZSB0aW1lIGZvciBhbmFseXNpcywgc2VsZWN0IHRoZSAxMDAwIG1vc3Qgc3VjY2Vzc2Z1bCBwcm9qZWN0cyBhbmQgYSBzYW1wbGUgb2YgMTAwMCB1bnN1Y2Nlc3NmdWwgcHJvamVjdHMuIFVzZSB0aGUgY2xlYW5pbmcgZnVuY3Rpb25zIGludHJvZHVjZWQgaW4gbGVjdHVyZSAob3Igd3JpdGUgeW91ciBvd24gaW4gYWRkaXRpb24pIHRvIHJlbW92ZSB1bm5lY2Vzc2FyeSB3b3JkcyAoc3RvcCB3b3JkcyksIHN5bnRheCwgcHVuY3R1YXRpb24sIG51bWJlcnMsIHdoaXRlIHNwYWNlIGV0Yy4gTm90ZSwgdGhhdCBtYW55IHByb2plY3RzIHVzZSB0aGVpciBvd24gdW5pcXVlIGJyYW5kIG5hbWVzIGluIHVwcGVyIGNhc2VzLCBzbyB0cnkgdG8gcmVtb3ZlIHRoZXNlIGZ1bGx5IGNhcGl0YWxpemVkIHdvcmRzIGFzIHdlbGwgKHNpbmNlIHdlIGFyZSBhaW1pbmcgdG8gaWRlbnRpZnkgY29tbW9uIHdvcmRzIGFjcm9zcyBkZXNjcmlwdGlvbnMpLiBDcmVhdGUgYSBkb2N1bWVudC10ZXJtLW1hdHJpeC4KClByb3ZpZGUgYSB3b3JkIGNsb3VkIG9mIHRoZSBtb3N0IGZyZXF1ZW50IG9yIGltcG9ydGFudCB3b3JkcyAoeW91ciBjaG9pY2Ugd2hpY2ggZnJlcXVlbmN5IG1lYXN1cmUgeW91IGNob29zZSkgYW1vbmcgdGhlIG1vc3Qgc3VjY2Vzc2Z1bCBwcm9qZWN0cy4KCgpgYGB7ciBoZWxwZXIgZnVuY3Rpb25zfQpyZW1vdmVOdW1QdW5jdCA8LSBmdW5jdGlvbih4KXtnc3ViKCJbXls6YWxwaGE6XVs6c3BhY2U6XVstXV0qIiwgIiIsIHgpfQoKIyBteSBvd24gZnVuY3Rpb25zCnJlbW92ZUJyYW5kIDwtIGZ1bmN0aW9uICh4KSB7Z3N1YigiXFxiW0EtWl0rXFxiIiwgIiIsIHgpfQpzdWJEYXNoIDwtIGZ1bmN0aW9uICh4KSB7Z3N1YigiWy1dIiwgIiAiLCB4KX0KCiMgZnVuY3Rpb24gc2VlbiBpbiBjbGFzcwpjbGVhbl9jb3JwdXMgPC0gZnVuY3Rpb24oY29ycHVzKXsKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcihyZW1vdmVCcmFuZCkpCiAgY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIGNvbnRlbnRfdHJhbnNmb3JtZXIocmVtb3ZlTnVtUHVuY3QpKQogIGNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCBjb250ZW50X3RyYW5zZm9ybWVyKHN1YkRhc2gpKQogIGNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCBjb250ZW50X3RyYW5zZm9ybWVyKHJlbW92ZVB1bmN0dWF0aW9uKSkKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIGMoc3RvcHdvcmRzKCJlbiIpKSkKICAgICMgT3B0aW9uYWxseSwgb25lIGNvdWxkIGFkZCBtb3JlIHN0b3Agd29yZHMKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgc3RyaXBXaGl0ZXNwYWNlKQogIHJldHVybihjb3JwdXMpCn0KCiMgZm9sbG93aW5nIHRoZSBhZHZpY2Ugb2YgY29tcGFyaW5nIG9yaWdpbmFsIHRleHQgYW5kIGNsZWFuZWQKIyBkZl9jbGVhbmVkIDwtIGNsZWFuX2NvcnB1cyhkZikKCiMgZmluYWxseSB0aGUgc3RlbW1pbmcgcHJvY2VzcwpzdGVtQ29tcGxldGlvbjIgPC0gZnVuY3Rpb24oeCwgZGljdGlvbmFyeSkgewogICB4IDwtIHVubGlzdChzdHJzcGxpdChhcy5jaGFyYWN0ZXIoeCksICIgIikpCiAgIHggPC0geFt4ICE9ICIiXQogICB4IDwtIHN0ZW1Db21wbGV0aW9uKHgsIGRpY3Rpb25hcnk9ZGljdGlvbmFyeSkKICAgeCA8LSBwYXN0ZSh4LCBzZXA9IiIsIGNvbGxhcHNlPSIgIikKICAgUGxhaW5UZXh0RG9jdW1lbnQoc3RyaXBXaGl0ZXNwYWNlKHgpKQp9CmBgYAoKYGBge3J9Cm1vc3Rfc3VjY2Vzc2Z1bF9waiA8LSBkZiAlPiUKICBmaWx0ZXIoc3RhdGUgPT0gInN1Y2Nlc3NmdWwiKSAlPiUKICBhcnJhbmdlKGRlc2MoYHBsZWRnZWRgKSkgJT4lCiAgaGVhZCgxMDAwKQoKdW5zdWNjZXNzZnVsX3BqIDwtIGRmICU+JQogIGZpbHRlcihzdGF0ZSA9PSAiZmFpbGVkIikgJT4lCiAgc2FtcGxlX24oMTAwMCkKYGBgCgpgYGB7cn0KZGZfc291cmNlX3N1Y2Nlc3NmdWwgPC0gbW9zdF9zdWNjZXNzZnVsX3BqICU+JQogIGRwbHlyOjpzZWxlY3QoYGlkYCwgYGJsdXJiYCkgJT4lCiAgcmVuYW1lKGRvY19pZCA9IGlkLCB0ZXh0ID0gYmx1cmIpCmBgYAoKYGBge3J9CmRmX3NvdXJjZV91biA8LSB1bnN1Y2Nlc3NmdWxfcGogJT4lCiAgZHBseXI6OnNlbGVjdChgaWRgLCBgYmx1cmJgKSAlPiUKICByZW5hbWUoZG9jX2lkID0gaWQsIHRleHQgPSBibHVyYikKYGBgCgpgYGB7cn0KVkNvcnB1cyhEYXRhZnJhbWVTb3VyY2UoZGZfc291cmNlX3N1Y2Nlc3NmdWwpKQpgYGAKCmBgYHtyfQpzX2NvcnB1c19yYXcgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShkZl9zb3VyY2Vfc3VjY2Vzc2Z1bCR0ZXh0KSkKc19jb3JwdXNfcmF3JG1ldGEkaWQgPC0gbW9zdF9zdWNjZXNzZnVsX3BqJGlkCgpzX2NvcnB1c19yYXdbWzFdXSRjb250ZW50CnNfY29ycHVzX3Jhd1tbMl1dJGNvbnRlbnQKc19jb3JwdXNfcmF3W1szXV0kY29udGVudApgYGAKYGBge3J9CnVfY29ycHVzX3JhdyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKGRmX3NvdXJjZV91biR0ZXh0KSkKdV9jb3JwdXNfcmF3JG1ldGEkaWQgPC0gdW5zdWNjZXNzZnVsX3BqJGlkCmBgYAoKCmBgYHtyfQpzX2NvcnB1c19jbGVhbmVkIDwtIGNsZWFuX2NvcnB1cyhzX2NvcnB1c19yYXcpCgpzX2NvcnB1c19jbGVhbmVkW1sxXV0kY29udGVudApzX2NvcnB1c19jbGVhbmVkW1syXV0kY29udGVudApzX2NvcnB1c19jbGVhbmVkW1szXV0kY29udGVudApgYGAKCgpgYGB7cn0Kc19jb3JwdXNfc3RlbW1lZCA8LSB0bV9tYXAoc19jb3JwdXNfY2xlYW5lZCwgc3RlbURvY3VtZW50KQpzX2NvcnB1c19maW5hbCA8LSB0bV9tYXAoc19jb3JwdXNfc3RlbW1lZCwgc3RlbUNvbXBsZXRpb24yLCBkaWN0aW9uYXJ5ID0gc19jb3JwdXNfY2xlYW5lZCkKYGBgCmBgYHtyfQp1X2NvcnB1c19jbGVhbmVkIDwtIGNsZWFuX2NvcnB1cyh1X2NvcnB1c19yYXcpCnVfY29ycHVzX3N0ZW1tZWQgPC0gdG1fbWFwKHVfY29ycHVzX2NsZWFuZWQsIHN0ZW1Eb2N1bWVudCkKdV9jb3JwdXNfZmluYWwgPC0gdG1fbWFwKHVfY29ycHVzX3N0ZW1tZWQsIHN0ZW1Db21wbGV0aW9uMiwgZGljdGlvbmFyeSA9IHVfY29ycHVzX2NsZWFuZWQpCmBgYAoKYGBge3J9CnN1Y2Nlc3NmdWxfZHRtIDwtIERvY3VtZW50VGVybU1hdHJpeChzX2NvcnB1c19maW5hbCkKc3VjY2Vzc2Z1bF9kdG0kZGltbmFtZXMkRG9jcyA8LSBhcy5jaGFyYWN0ZXIoc19jb3JwdXNfZmluYWwkbWV0YSRpZCkKYGBgCgpgYGB7cn0KdW5fZHRtIDwtIERvY3VtZW50VGVybU1hdHJpeCh1X2NvcnB1c19maW5hbCkKdW5fZHRtJGRpbW5hbWVzJERvY3MgPC0gYXMuY2hhcmFjdGVyKHVfY29ycHVzX2ZpbmFsJG1ldGEkaWQpCmBgYAoKYGBge3J9CnN1Y2Nlc3NmdWxfZHRtX3RpZHkgPC0gdGlkeShzdWNjZXNzZnVsX2R0bSkKCnN1Y2Nlc3NmdWxfdGVybXMgPC0gc3VjY2Vzc2Z1bF9kdG1fdGlkeSAlPiUKICBncm91cF9ieSh0ZXJtKSAlPiUKICBzdW1tYXJpc2UobiA9IHN1bShjb3VudCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgdG9wX24oMTAwKQpgYGAKCmBgYHtyfQp1bl9kdG1fdGlkeSA8LSB0aWR5KHVuX2R0bSkKCnVuc3VjY2Vzc2Z1bF90ZXJtcyA8LSB1bl9kdG1fdGlkeSAlPiUKICBncm91cF9ieSh0ZXJtKSAlPiUKICBzdW1tYXJpc2UobiA9IHN1bShjb3VudCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgdG9wX24oMTAwKQpgYGAKCmBgYHtyfQpteV9jb2xvcnMyIDwtIG15X2NvbG9yc1tjKDgsMSwxNCldCmBgYAoKYGBge3J9CndvcmRjbG91ZCh3b3JkcyA9IHN1Y2Nlc3NmdWxfdGVybXMkdGVybSwgCiAgICAgICAgICBmcmVxID0gYXMuaW50ZWdlcihzdWNjZXNzZnVsX3Rlcm1zJG4pLAogICAgICAgICAgc2NhbGUgPSBjKDQsIC41KSwKICAgICAgICAgIG1pbi5mcmVxID0gMCwgCiAgICAgICAgICBtYXgud29yZHMgPSAxMDAsCiAgICAgICAgICBjb2xvcnMgPSBteV9jb2xvcnMyCiAgICAgICAgICApCmBgYAoKU2luY2UgSSd2ZSBhbHJlYWR5IGRvbmUgbW9zdCBvZiB0aGUgd29yaywgSSdtIGludGVyZXN0ZWQgaW4gYSBjb21wYXJpc29uIHdpdGggZmFpbGVkIHByb2plY3RzLgoKYGBge3J9CndvcmRjbG91ZCh3b3JkcyA9IHVuc3VjY2Vzc2Z1bF90ZXJtcyR0ZXJtLCAKICAgICAgICAgIGZyZXEgPSBhcy5pbnRlZ2VyKHVuc3VjY2Vzc2Z1bF90ZXJtcyRuKSwKICAgICAgICAgIHNjYWxlID0gYyg0LCAuNSksCiAgICAgICAgICBtaW4uZnJlcSA9IDAsIAogICAgICAgICAgbWF4LndvcmRzID0gMTAwLAogICAgICAgICAgY29sb3JzID0gbXlfY29sb3JzCiAgICAgICAgICApCmBgYAoKQXBwYXJlbnRseSBhIGNyeSBmb3IgaGVscCBkb2Vzbid0IGhlbHAhIE5ldyBhcHBlYXJzIHRvIGJlIGVxdWFsbHkgZnJlcXVlbnQgaW4gYm90aC4gV2hpbGUgZmlyc3QgaXMgYSBzaWduYXR1cmUgZm9yIHN1Y2Nlc3NmdWwgcHJvamVjdHMhCgojIyMgYikgU3VjY2VzcyBpbiB3b3JkcwoKUHJvdmlkZSBhIHB5cmFtaWQgcGxvdCB0byBzaG93IGhvdyB0aGUgd29yZHMgYmV0d2VlbiBzdWNjZXNzZnVsIGFuZCB1bnN1Y2Nlc3NmdWwgcHJvamVjdHMgZGlmZmVyIGluIGZyZXF1ZW5jeS4gQSBzZWxlY3Rpb24gb2YgMTAgLSAyMCB0b3Agd29yZHMgaXMgc3VmZmljaWVudCBoZXJlLiAKCmBgYHtyfQphbGxfc3VjY2Vzc2Z1bF90ZXJtcyA8LSBzdWNjZXNzZnVsX2R0bV90aWR5ICU+JQogIGdyb3VwX2J5KHRlcm0pICU+JQogIHN1bW1hcmlzZShuID0gc3VtKGNvdW50KSkgJT4lCiAgYXJyYW5nZShkZXNjKG4pKQoKYWxsX3Vuc3VjY2Vzc2Z1bF90ZXJtcyA8LSB1bl9kdG1fdGlkeSAlPiUKICBncm91cF9ieSh0ZXJtKSAlPiUKICBzdW1tYXJpc2UobiA9IHN1bShjb3VudCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgpgYGB7cn0KdGVybXMgPC0gbWVyZ2UoYWxsX3N1Y2Nlc3NmdWxfdGVybXMsIGFsbF91bnN1Y2Nlc3NmdWxfdGVybXMsIGJ5ID0gInRlcm0iKQp0ZXJtc19mb3JfcGxvdCA8LSB0ZXJtcyAlPiUgCiAgZmlsdGVyKG4ueCA+IDMwIHwgbi55ID4zMCkKdGVybXNfZm9yX3Bsb3QgPC0gdGVybXNfZm9yX3Bsb3RbYygxLCA3LCAxNiwgMTksIDI0LCAyNywgMjAsIDI5LCAzMywgMzUsIDM2LCA1MCwgNTIsIDUzLCA0MSwgNDIsIDQ4LCAxMiwgOSwgMyksXQpgYGAKCmBgYHtyfQpzY29sPC0gbXlfY29sb3JzWzZdCnVjb2w8LSBteV9jb2xvcnNbMTRdCmBgYAoKYGBge3J9CnB5cmFtaWQucGxvdCh0ZXJtc19mb3JfcGxvdCRuLngsIHRlcm1zX2Zvcl9wbG90JG4ueSwgbGFiZWxzPXRlcm1zX2Zvcl9wbG90JHRlcm0sCiAgbWFpbj0iV29yZHMgZnJlcXVlbmN5IHB5cmFtaWQgcGxvdCIsCiAgdG9wLmxhYmVscyA9IGMoIlN1Y2Nlc3NmdWwiLCAiVGVybSIsICJVbnN1Y2Nlc3NmdWwiKSwKICBseGNvbD1zY29sLHJ4Y29sPXVjb2wsCiAgbGF4bGFiID0gTlVMTCwKICAgICAgICAgICAgIHJheGxhYiA9IE5VTEwsCiAgICAgICAgICAgICB1bml0ID0gTlVMTCwKICAgICAgICAgICAgIGxhYmVsY2V4PTAuNSwKICBnYXA9MjMsc2hvdy52YWx1ZXM9VFJVRSkKYGBgCgpJdCdzIGEgdmVyeSBpbnRlcmVzdGluZyBncmFwaCBmcm9tIGEgcHN5Y2hvbG9naWNhbCBwZXJzcGVjdGl2ZS4gVGhlIG1vcmUgdGhlIHdvcmRzIGFyZSAibmVlZHkiIHRoZSBtb3JlIGlzIGxpa2VseSB0aGUgcHJvamVjdCBpcyBnb2luZyB0byBmYWlsLiBUaGUgc2FtZSBjYW4gYmUgc2FpZCBmb3IgdGhvc2UgcHJvamVjdHMgaW4gd2hpY2ggdGhpbmdzIGFyZSBub3QgZG9uZSB5ZXQuIFdvcmRzIGxpa2UgIndpbGwiICJtYWtlIiBhcmUgYXNzb2NpYXRlZCB3aXRoIGZhaWx1cmUuCgoKIyMjIGMpIFNpbXBsaWNpdHkgYXMgYSB2aXJ0dWUKClRoZXNlIGJsdXJicyBhcmUgc2hvcnQgaW4gbGVuZ3RoIChtYXguIDE1MCBjaGFyYWN0ZXJzKSBidXQgbGV0J3Mgc2VlIHdoZXRoZXIgYnJldml0eSBhbmQgc2ltcGxpY2l0eSBzdGlsbCBtYXR0ZXJzLiBDYWxjdWxhdGUgYSByZWFkYWJpbGl0eSBtZWFzdXJlIChGbGVzaCBSZWFkaW5nIEVhc2UsIEZsZXNoIEtpbmNhaWQgb3IgYW55IG90aGVyIGNvbXBhcmFibGUgbWVhc3VyZSkgZm9yIHRoZSB0ZXh0cy4gVmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVhZGFiaWxpdHkgbWVhc3VyZSBhbmQgb25lIG9mIHRoZSBtZWFzdXJlcyBvZiBzdWNjZXNzLiBCcmllZmx5IGNvbW1lbnQgb24geW91ciBmaW5kaW5nLgoKYGBge3J9CnNfcGogPC0gbW9zdF9zdWNjZXNzZnVsX3BqICU+JQogIGRwbHlyOjpzZWxlY3QoYGJsdXJiYCwgYHN0YXRlYCwgYGdvYWxgLCBgYWNoaWV2ZW1lbnRfcmF0aW9gKSAlPiUKICBmaWx0ZXIoYGdvYWxgID4gMTAwMCkKCnVfcGogPC0gdW5zdWNjZXNzZnVsX3BqICU+JQogIGRwbHlyOjpzZWxlY3QoYGJsdXJiYCwgYHN0YXRlYCwgYGdvYWxgLCBgYWNoaWV2ZW1lbnRfcmF0aW9gKSAlPiUKICBmaWx0ZXIoYGdvYWxgID4gMTAwMCkKCmRmX21lcmdlZCA8LSByYmluZChzX3BqLCB1X3BqKQpgYGAKCmBgYHtyfQpyZWFkYWJpbGl0eV9zY29yZSA8LSB0ZXh0c3RhdF9yZWFkYWJpbGl0eShhcy5jaGFyYWN0ZXIoZGZfbWVyZ2VkJGJsdXJiKSwgCiAgICAgICAgbWVhc3VyZSA9IGMoJ0ZsZXNjaCcsJ0ZsZXNjaC5LaW5jYWlkJywKICAgICAgICAgICAgICAgICAgJ21lYW5TZW50ZW5jZUxlbmd0aCcsJ21lYW5Xb3JkU3lsbGFibGVzJykpCgpyZWFkYWJpbGl0eV9zY29yZVtjb2xuYW1lcyhkZl9tZXJnZWQpXSA8LSBkZl9tZXJnZWQKYGBgCgpgYGB7cn0KcmVhZGFiaWxpdHlfc2NvcmUgJT4lCiAgZmlsdGVyKGxvZyhhY2hpZXZlbWVudF9yYXRpbykgPiAtMTApICU+JQogIAogIGdncGxvdChhZXMoeCA9IGxvZyhhY2hpZXZlbWVudF9yYXRpbyksIHkgPSBGbGVzY2guS2luY2FpZCwgY29sb3IgPSBzdGF0ZSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKGNvbG9yID0gbXlfY29sb3JzWzhdKSArCiAgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGModWNvbCwgc2NvbCkpICsKICAKICBsYWJzKAogICAgdGl0bGUgPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gUmVhZGFiaWxpdHkgYW5kIFN1Y2Nlc3MiLCAKICAgIHggPSAibG9nIEFjaGlldmVtZW50IFJhdGlvIiwgCiAgICB5ID0gIkZsZXNjaC1LaW5jYWlkIEdyYWRlIExldmVsIiwKICAgIGNvbG9yID0gIlN0YXRlIgogICAgKSArCiAgCiAgCiAgdGhlbWVfdHVmdGUoKSArIAogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCwgZmFjZSA9ICJpdGFsaWMiKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpLAogICAgCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjQ3NSksCiAgICAKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJ3aGl0ZSIpLAogICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoKQogICAgKQpgYGAKClRoZXJlIGlzIGEgc2xpZ2h0bHksIGJ1dCBuZWdsaWdpYmxlLCBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBoYXZpbmcgYSBkZXNjcmlwdGlvbiB3aXRoIGFuIGhpZ2hlciBGbGVzY2gtS2luY2FpZCBzY29yZSBhbmQgaGF2aW5nIGFuIGhpZ2hlciBBY2hpZXZlbWVudCBSYXRpbyAoTW9uZXkgcGxlZGdlZCAvIEdvYWwsIHdoZW4gR29hbCA+IDEwMDApLiBJIGZpbHRlcmVkIGZvciBHb2FsID4gMTAwMCBiZWNhdXNlIG1hbnkgcHJvamVjdHMgc2V0IHZlcnkgbG93ICh1bnJlYWxpc3RpYykgZ29hbHMuIFRoZSBGbGVzY2gtS2luY2FpZCBzY2FsZSBtZWFzdXJlcyBob3cgZWFzaWx5IHJlYWRhYmxlIGEgdGV4dCBpcy4gVGhlIGhpZ2hlciB0aGUgc2NvcmUgdGhlIGVhc2llciB0aGUgdGV4dCBpcyBlYXN5IHRvIHJlYWQuCgoKIyMgMy4gU2VudGltZW50CgpOb3csIGxldCdzIGNoZWNrIHdoZXRoZXIgdGhlIHVzZSBvZiBwb3NpdGl2ZSAvIG5lZ2F0aXZlIHdvcmRzIG9yIHNwZWNpZmljIGVtb3Rpb25zIGhlbHBzIGEgcHJvamVjdCB0byBiZSBzdWNjZXNzZnVsLiAKCiMjIyBhKSBTdGF5IHBvc2l0aXZlCgpDYWxjdWxhdGUgdGhlIHRvbmUgb2YgZWFjaCB0ZXh0IGJhc2VkIG9uIHRoZSBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgd29yZHMgdGhhdCBhcmUgYmVpbmcgdXNlZC4gWW91IGNhbiByZWx5IG9uIHRoZSBIdSAmIExpdSBkaWN0aW9uYXJ5IHByb3ZpZGVkIGluIGxlY3R1cmUgb3IgdXNlIHRoZSBCaW5nIGRpY3Rpb25hcnkgY29udGFpbmVkIGluIHRoZSB0aWR5dGV4dCBwYWNrYWdlIChgdGlkeXRleHQ6OnNlbnRpbWVudHNgKS4gVmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0b25lIG9mIHRoZSBkb2N1bWVudCBhbmQgc3VjY2Vzcy4gQnJpZWZseSBjb21tZW50LgoKQWZ0ZXIgY29uc3VsdGluZyBodHRwczovL3d3dy50aWR5dGV4dG1pbmluZy5jb20vc2VudGltZW50Lmh0bWwsIEkgc2VsZWN0ZWQgdGhlICJucmMiIGRpY3Rpb25hcnkgZnJvbSBTYWlmIE1vaGFtbWFkIGFuZCBQZXRlciBUdXJuZXkgYXMgaXQncyB0aGUgb25lIHdpdGggbW9yZSB3b3Jkcy4KCmBgYHtyfQpkYXRhZnJhbWUxIDwtIGRhdGEuZnJhbWUodGV4dD1zYXBwbHkoc19jb3JwdXNfY2xlYW5lZCwgaWRlbnRpdHkpLCAKICAgIHN0cmluZ3NBc0ZhY3RvcnM9RikKZGF0YWZyYW1lMSA8LSBkYXRhZnJhbWUxICU+JSAKICBtdXRhdGUoaW5kZXggPSByb3dfbnVtYmVyKCkpCgpkZjEuMSA8LSBtb3N0X3N1Y2Nlc3NmdWxfcGogJT4lCiAgZHBseXI6OnNlbGVjdChgc3RhdGVgLCBgZ29hbGAsIGBhY2hpZXZlbWVudF9yYXRpb2ApICU+JQogIG11dGF0ZShpbmRleCA9IHJvd19udW1iZXIoKSkKCmRhdGFmcmFtZTEuMiA8LSBsZWZ0X2pvaW4oZGF0YWZyYW1lMSwgZGYxLjEsIGJ5ID0gImluZGV4IikKYGBgCgpgYGB7cn0KZGF0YWZyYW1lMiA8LSBkYXRhLmZyYW1lKHRleHQ9c2FwcGx5KHVfY29ycHVzX2NsZWFuZWQsIGlkZW50aXR5KSwgCiAgICBzdHJpbmdzQXNGYWN0b3JzPUYpCmRhdGFmcmFtZTIgPC0gZGF0YWZyYW1lMiAlPiUgCiAgbXV0YXRlKGluZGV4ID0gcm93X251bWJlcigpKQoKZGYyLjEgPC0gdW5zdWNjZXNzZnVsX3BqICU+JQogIGRwbHlyOjpzZWxlY3QoYHN0YXRlYCwgYGdvYWxgLCBgYWNoaWV2ZW1lbnRfcmF0aW9gKSAlPiUKICBtdXRhdGUoaW5kZXggPSByb3dfbnVtYmVyKCkpCgpkYXRhZnJhbWUyLjIgPC0gbGVmdF9qb2luKGRhdGFmcmFtZTIsIGRmMi4xLCBieSA9ICJpbmRleCIpCmBgYAoKYGBge3J9CmRmX21lcmdlZF9jbGVhbmVkIDwtIHJiaW5kKGRhdGFmcmFtZTEuMiwgZGF0YWZyYW1lMi4yKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHN5dXpoZXQpCgpwb2xhcml0eV9zY29yZSA8LSBnZXRfc2VudGltZW50KGRmX21lcmdlZF9jbGVhbmVkJHRleHQsIG1ldGhvZD0ibnJjIikKCmRmX21lcmdlZF9jbGVhbmVkJHNlbnRpbWVudCA8LSBwb2xhcml0eV9zY29yZQpgYGAKCmBgYHtyfQpsb2coMTAwKQpgYGAKCldoZW4gQWNoaWV2ZW1lbnQgUmF0ZSA9IDEwMCB0aGUgcHJvamVjdCByZWFjaGVkIGl0cyBnb2FsLgoKYGBge3J9CmRmX21lcmdlZF9jbGVhbmVkICU+JSAKICBmaWx0ZXIoZ29hbCA+IDEwMDApICU+JQogIGZpbHRlcihsb2coYWNoaWV2ZW1lbnRfcmF0aW8pID4gLTgpICU+JQogCiAgZ2dwbG90KGFlcyh4ID0gc2VudGltZW50LCB5ID0gbG9nKGFjaGlldmVtZW50X3JhdGlvKSwgY29sb3IgPSBzdGF0ZSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChjb2xvciA9IG15X2NvbG9yc1s4XSkgKwogIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDQuNjA1MTcpICsKICBnZW9tX2xhYmVsKAogICAgbGFiZWw9IkdvYWwgcmVhY2hlZCIsIAogICAgeD02LjM1LAogICAgeT02LAogICAgbGFiZWwucGFkZGluZyA9IHVuaXQoMC40NSwgImxpbmVzIiksCiAgICBsYWJlbC5zaXplID0gMC4xNSwKICAgIGNvbG9yID0gImJsYWNrIiwKICAgIGZpbGw9ImhvbmV5ZGV3MiIKICApICsKICAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyh1Y29sLCBzY29sKSkgKwogIAogIGxhYnMoCiAgICB0aXRsZSA9ICJSZWxhdGlvbnNoaXAgYmV0d2VlbiBUZXh0IFNlbnRpbWVudCBhbmQgU3VjY2VzcyIsIAogICAgeCA9ICJUZXh0IFNlbnRpbWVudCIsIAogICAgeSA9ICJsb2cgQWNoaWV2ZW1lbnQgUmF0aW8iLAogICAgY29sb3IgPSAiU3RhdGUiCiAgICApICsKICAKICAKICB0aGVtZV90dWZ0ZSgpICsgCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgZmFjZSA9ICJib2xkIiksCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBmYWNlID0gIml0YWxpYyIpLAogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCksCiAgICAKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNDc1KSwKICAgIAogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gIndoaXRlIiksCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpCiAgICApCmBgYAoKVGhpcyBpcyBhIHdlaXJkIHJlc3VsdC4gQXBwYXJlbnRseSwgb25jZSB0aGUgdGV4dCBvZiBhIHByb2plY3QgZ29lcyBiZXlvbmQgYSBzbGlnaHRseSBwb3NpdGl2ZSBzZW50aW1lbnQsIGl0cyBzdWNjZXNzIGNoYW5jZXMgZGVjcmVhc2UuIEhvd2V2ZXIsIGl0IG1pZ2h0IG1ha2Ugc2Vuc2UhIEV4dHJlbWVseSBwb3NpdGl2ZSB0ZXh0cyBtaWdodCBiZSBzdXNwaWNpb3VzLiBQcm9qZWN0cyBtaWdodCBhcHBlYXIgdG9vIGdvb2QgdG8gYmUgdHJ1ZS4KCiMjIyBiKSBQb3NpdGl2ZSB2cyBuZWdhdGl2ZQoKU2VncmVnYXRlIGFsbCAyLDAwMCBibHVyYnMgaW50byBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgdGV4dHMgYmFzZWQgb24gdGhlaXIgcG9sYXJpdHkgc2NvcmUgY2FsY3VsYXRlZCBpbiBzdGVwIChhKS4gTm93LCBjb2xsYXBzZSB0aGUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHRleHRzIGludG8gdHdvIGxhcmdlciBkb2N1bWVudHMuIENyZWF0ZSBhIGRvY3VtZW50LXRlcm0tbWF0cml4IGJhc2VkIG9uIHRoaXMgY29sbGFwc2VkIHNldCBvZiB0d28gZG9jdW1lbnRzLiBHZW5lcmF0ZSBhIGNvbXBhcmlzb24gY2xvdWQgc2hvd2luZyB0aGUgbW9zdC1mcmVxdWVudCBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgd29yZHMuICAKCmBgYHtyfQpkZl9tZXJnZWRfY2xlYW5lZCRwb2xhcml0eSA8LSBpZmVsc2UoZGZfbWVyZ2VkX2NsZWFuZWQkc2VudGltZW50IDwgMCwgIk5lZ2F0aXZlIiwgaWZlbHNlKGRmX21lcmdlZF9jbGVhbmVkJHNlbnRpbWVudCA+PSAwLCAiUG9zaXRpdmUiLCBOQSkpCmBgYAoKYGBge3J9CmRmX3Bvc2l0aXZlIDwtIGRmX21lcmdlZF9jbGVhbmVkICU+JQogIGZpbHRlcihwb2xhcml0eSA9PSAiUG9zaXRpdmUiKQoKZGZfbmVnYXRpdmUgPC0gZGZfbWVyZ2VkX2NsZWFuZWQgJT4lCiAgZmlsdGVyKHBvbGFyaXR5ID09ICJOZWdhdGl2ZSIpCmBgYAoKYGBge3J9CmRmXzNiMSA8LSB1bm5lc3RfdG9rZW5zKGRmX21lcmdlZF9jbGVhbmVkLCBvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IHRleHQpIAoKZGZfM2IgPC0gZGZfM2IxICU+JQogIGdyb3VwX2J5KHBvbGFyaXR5LCB3b3JkKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGNhc3RfdGRtKHdvcmQsIHBvbGFyaXR5LCBjb3VudCkKCm1hdHJpeDNiIDwtIGFzLm1hdHJpeChkZl8zYikKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMjEwNSkKY29tcGFyaXNvbi5jbG91ZChtYXRyaXgzYiwgCiAgICAgICAgICAgICAgICAgY29sb3JzID0gYyh1Y29sLCBzY29sKSwgCiAgICAgICAgICAgICAgICAgdGl0bGUuc2l6ZT0gMS4zLAogICAgICAgICAgICAgICAgIG1heC53b3JkcyA9IDEwMCwKICAgICAgICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSkKYGBgCgojIyMgYykgR2V0IGluIHRoZWlyIG1pbmQKCk5vdywgdXNlIHRoZSBOUkMgV29yZC1FbW90aW9uIEFzc29jaWF0aW9uIExleGljb24gaW4gdGhlIGB0aWR5dGV4dGAgcGFja2FnZSB0byBpZGVudGlmeSBhIGxhcmdlciBzZXQgb2YgZW1vdGlvbnMgKGFuZ2VyLCBhbnRpY2lwYXRpb24sIGRpc2d1c3QsIGZlYXIsIGpveSwgc2FkbmVzcywgc3VycHJpc2UsIHRydXN0KS4gQWdhaW4sIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHVzZSBvZiB3b3JkcyBmcm9tIHRoZXNlIGNhdGVnb3JpZXMgYW5kIHN1Y2Nlc3MuIFdoYXQgaXMgeW91ciBmaW5kaW5nPwoKYGBge3J9CnNlbnRpbWVudHMgPC0gZ2V0X3NlbnRpbWVudHMoIm5yYyIpCmBgYAoKYGBge3J9CmRmX3NlbnRpbWVudHMgPC0gIGlubmVyX2pvaW4oZGZfM2IxLCBzZW50aW1lbnRzLCBieSA9ICJ3b3JkIikKYGBgCgpgYGB7cn0KZGZfc2VudGltZW50cyAlPiUKICAKICBnZ3Bsb3QoYWVzKHggPSBzZW50aW1lbnQueSwgeSA9IGFjaGlldmVtZW50X3JhdGlvLCBjb2xvciA9IHN0YXRlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHVjb2wsIHNjb2wpKSArCiAgCiAgbGFicygKICAgIHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIFNlbnRpbWVudHMgYW5kIFN1Y2Nlc3MiLCAKICAgIHggPSAiU2VudGltZW50cyIsIAogICAgeSA9ICJBY2hpZXZlbWVudCBSYXRpbyIsCiAgICBjb2xvciA9ICJTdGF0ZSIKICAgICkgKwogIAogIAogIHRoZW1lX3R1ZnRlKCkgKyAKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLCBmYWNlID0gImJvbGQiKSwKICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIGZhY2UgPSAiaXRhbGljIiksCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoKSwKICAgIAogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gIndoaXRlIiksCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdCgpCiAgICApCmBgYAoKVGhlIG1vc3Qgc3VjY2Vzc2Z1bCBwcm9qZWN0cyBoYXZlIHdvcmRzIHRoYXQgaW5kdWNlIHBvc2l0aXZlIGVtb3Rpb25zIGxpa2UgQW50aWNpcGF0aW9uLCBKb3ksIFBvc2l0aXZlLCBTdXJwcmlzZSwgYW5kIFRydXN0LiBUaGVyZSBpcyBhbHNvIGEgc3VjY2Vzc2Z1bCBwcm9qZWN0IHRoYXQgaGFzIEFuZ2VyIGFuZCBOZWdhdGl2ZS4gSG93ZXZlciwgbm9uZSBvZiB0aGUgbW9zdCBzdWNjZXNzZnVsIGhhcyBEaXNndXN0IG9yIEZlYXIgb3IgU2FkbmVzcy4gTW9yZW92ZXIsIFBvc2l0aXZlIGFuZCBUcnVzdCBzZWVtIHRvIGJlIHRoZSB0d28gd2l0aCB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbiB3aXRoIHRoZSBBY2hpZXZlbWVudCBSYXRpby4=